package com.demo.controller.admin;

import com.demo.dao.NewsDao;
import com.demo.entity.News;
import com.demo.service.NewsService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.*;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.IntStream;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test") // 使用测试配置文件
@Transactional // 测试后事务回滚
public class AdminNewsControllerIT {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private NewsService newsService;

    @Autowired
    private NewsDao newsDao;

    // 测试常量定义
    private static final int TOTAL_NEWS = 50;

    @BeforeEach
    void setUp() {
        // 清空现有数据
        newsDao.deleteAll();

        // 基础时间点（用于生成时间序列）
        LocalDateTime baseTime = LocalDateTime.now();

        IntStream.rangeClosed(1, TOTAL_NEWS).forEach(i -> {
            News news = new News(
                    i,
                    "Title" + i,
                    "Content" + i,
                    baseTime.minusDays(i) // 按天数递减的时间
            );
            newsDao.save(news);
        });

        // 验证数据插入
        assertAll(
                () -> assertEquals(TOTAL_NEWS, newsDao.count(), "总新闻数量不匹配")
        );
    }


    @Test
    public void newsManage_ShouldReturnViewWithTotalPages0() throws Exception {
        mockMvc.perform(get("/news_manage"))
                .andExpect(status().isOk())
                .andExpect(view().name("admin/news_manage"))
                .andExpect(model().attribute("total", 5));
    }

    @Test
    public void newsManage_ShouldReturnViewWithTotalPages1() throws Exception {
        int existingId = newsDao.findAll().get(0).getNewsID();
        newsDao.deleteById(existingId);

        mockMvc.perform(get("/news_manage"))
                .andExpect(status().isOk())
                .andExpect(view().name("admin/news_manage"))
                .andExpect(model().attribute("total", 5));
    }

    @Test
    public void newsManage_ShouldReturnViewWithTotalPages2() throws Exception {
        LocalDateTime baseTime = LocalDateTime.now();
        News news = new News(51, "Title51", "这是第51条新闻的内容", baseTime);
        newsDao.save(news);

        mockMvc.perform(get("/news_manage"))
                .andExpect(status().isOk())
                .andExpect(view().name("admin/news_manage"))
                .andExpect(model().attribute("total", 6));
    }

    @Test
    public void newsList_ShouldReturnPaginatedNews0() throws Exception {

        mockMvc.perform(get("/newsList.do")
                        .param("page", "1")) // 注意：Controller中page参数从1开始
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].title").value("Title1"))
                .andExpect(jsonPath("$[1].title").value("Title2"));
    }

    @Test
    public void newsList_ShouldReturnPaginatedNews1() throws Exception {

        mockMvc.perform(get("/newsList.do")) // 注意：Controller中page参数从1开始
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].title").value("Title1"))
                .andExpect(jsonPath("$[1].title").value("Title2"));
    }

    @Test
    public void newsList_ShouldReturnCorrectPageOfNews2() throws Exception {

        mockMvc.perform(get("/newsList.do")
                        .param("page", "3")) // 请求第三页
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].title").value("Title21"))  // 第三页的第一条新闻
                .andExpect(jsonPath("$[9].title").value("Title30")); // 第三页的最后一条新闻
    }

    @Test
    public void delNews_ShouldDeleteNewsAndReturnTrue() throws Exception {
        int existingId = newsDao.findAll().get(0).getNewsID();

        mockMvc.perform(post("/delNews.do")
                        .param("newsID", String.valueOf(existingId)))
                .andExpect(status().isOk())
                .andExpect(content().string("true"));

    }

    @Test
    public void modifyNews_ShouldUpdateNewsAndRedirect0() throws Exception {
        int newsID = newsDao.findAll().get(0).getNewsID();
        String newTitle = "Updated Title";
        String newContent = "Updated Content";

        mockMvc.perform(post("/modifyNews.do")
                        .param("newsID", String.valueOf(newsID))
                        .param("title", newTitle)
                        .param("content", newContent))
                .andExpect(status().is3xxRedirection())
                .andExpect(redirectedUrl("news_manage"));

        Optional<News> updatedNews = newsDao.findById(newsID);
        assertTrue(updatedNews.isPresent(), "新闻应该存在");
        assertEquals(newTitle, updatedNews.get().getTitle());
        assertEquals(newContent, updatedNews.get().getContent());
    }

    @Test
    public void modifyNews_ShouldUpdateNewsAndRedirect1() throws Exception {
        int newsID = newsDao.findAll().get(0).getNewsID();
        String newTitle = "";
        String newContent = "Updated Content";

        mockMvc.perform(post("/modifyNews.do")
                        .param("newsID", String.valueOf(newsID))
                        .param("title", newTitle)
                        .param("content", newContent))
                .andExpect(status().is3xxRedirection())
                .andExpect(redirectedUrl("news_manage"));

        Optional<News> updatedNews = newsDao.findById(newsID);
        assertTrue(updatedNews.isPresent(), "新闻应该存在");
        assertEquals(newTitle, updatedNews.get().getTitle());
        assertEquals(newContent, updatedNews.get().getContent());
    }

    @Test
    public void modifyNews_ShouldUpdateNewsAndRedirect2() throws Exception {
        int newsID = newsDao.findAll().get(0).getNewsID();
        String newTitle = "Updated Title";
        String newContent = "";

        mockMvc.perform(post("/modifyNews.do")
                        .param("newsID", String.valueOf(newsID))
                        .param("title", newTitle)
                        .param("content", newContent))
                .andExpect(status().is3xxRedirection())
                .andExpect(redirectedUrl("news_manage"));

        Optional<News> updatedNews = newsDao.findById(newsID);
        assertTrue(updatedNews.isPresent(), "新闻应该存在");
        assertEquals(newTitle, updatedNews.get().getTitle());
        assertEquals(newContent, updatedNews.get().getContent());
    }

    @Test
    public void addNews_ShouldCreateNewsAndRedirect() throws Exception {
        String title = "New Title";
        String content = "New Content";

        long countBefore = newsDao.count();  // 记录插入前的新闻数量

        mockMvc.perform(post("/addNews.do")
                        .param("title", title)
                        .param("content", content))
                .andExpect(status().is3xxRedirection())
                .andExpect(redirectedUrl("news_manage"));

        // 重新查询数据库，确保数据已经被插入
        long countAfter = newsDao.count();
        assertEquals(countBefore + 1, countAfter, "新闻数量应该增加 1");

        // 找到最新插入的新闻
        News latestNews = newsDao.findAll(Sort.by(Sort.Direction.DESC, "newsID")).get(0);

        // 验证新插入的新闻是否匹配
        assertEquals(title, latestNews.getTitle());
        assertEquals(content, latestNews.getContent());
    }

    @Test
    public void newsEdit_ShouldReturnEditViewWithNews() throws Exception {
        int newsID = newsDao.findAll().get(0).getNewsID();
        News news = newsService.findById(newsID);

        mockMvc.perform(get("/news_edit")
                        .param("newsID", String.valueOf(newsID)))
                .andExpect(status().isOk())
                .andExpect(view().name("/admin/news_edit"))
                .andExpect(model().attribute("news", news));
    }

    @Test
    public void newsAdd_ShouldReturnAddView() throws Exception {
        mockMvc.perform(get("/news_add"))
                .andExpect(status().isOk())
                .andExpect(view().name("/admin/news_add"));
    }
}
